/* Copyright  2009
   @Author
   Richard Changde Yin            e-mail yinchangde@hotmail.com

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; version 2 of the License.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */

/**
 *@filename: function/str/make_set.cc
 */
#include <server_includes.h>
#include CSTDINT_H
#include <function/str/make_set.h>
#include <session.h>

void Item_func_make_set::update_used_tables()
{
  Item_func::update_used_tables();
  item->update_used_tables();
  used_tables_cache    |=item->used_tables();
  const_item_cache     &=item->const_item ();
}


void Item_func_make_set::split_sum_func(Session *session,
          Item **ref_pointer_array,
					List<Item> &fields)
{
  item->split_sum_func(session, ref_pointer_array, fields, &item, true);
  Item_str_func::split_sum_func(session, ref_pointer_array, fields);
}


void Item_func_make_set::fix_length_and_dec()
{
  max_length=arg_count-1;

  if (agg_arg_charsets(collation, args, arg_count,COLLAPSAR_COLL_ALLOW_CONV, 1))
    return;

  for (uint32_t i=0 ; i < arg_count ; i++)
    max_length+=args[i]->max_length;

  used_tables_cache    |=	  item->used_tables();
  not_null_tables_cache&=   item->not_null_tables();
  const_item_cache     &=	  item->const_item();
  with_sum_func= with_sum_func || item->with_sum_func;
}

String *Item_func_make_set::val_str(String *str)
{
  assert(fixed == 1);
  uint64_t bits;
  bool first_found=0;
  Item **ptr=args;
  String *result=&my_empty_string;

  bits=item->val_int();
  if ((null_value=item->null_value))
    return NULL;

  if (arg_count < 64)
    bits &= ((uint64_t) 1 << arg_count)-1;

  for (; bits; bits >>= 1, ptr++)
  {
    if (bits & 1)
    {
      String *res= (*ptr)->val_str(str);
      if (res)					// Skip nulls
      {
      	if (!first_found)
      	{					// First argument
      	  first_found=1;
      	  if (res != str)
      	    result=res;				// Use original string
      	  else
      	  {
      	    if (tmp_str.copy(*res))		// Don't use 'str'
      	      return &my_empty_string;
      	    result= &tmp_str;
      	  }
      	}
      	else
      	{
      	  if (result != &tmp_str)
      	  {// Copy data to tmp_str
      	    if (tmp_str.alloc(result->length()+res->length()+1) ||
      		    tmp_str.copy(*result))
      	      return &my_empty_string;
      	    result= &tmp_str;
      	  }
      	  if (tmp_str.append(STRING_WITH_LEN(","), &my_charset_bin)
                                           || tmp_str.append(*res))
      	    return &my_empty_string;
      	}
      }
    }
  }
  return result;
}


Item *Item_func_make_set::transform(Item_transformer transformer, unsigned char *arg)
{
  Item *new_item= item->transform(transformer, arg);
  if (!new_item)
    return 0;

  /*
    Session::change_item_tree() should be called only if the tree was
    really transformed, i.e. when a new item has been created.
    Otherwise we'll be allocating a lot of unnecessary memory for
    change records at each execution.
  */
  if (item != new_item)
    current_session->change_item_tree(&item, new_item);
  return Item_str_func::transform(transformer, arg);
}


void Item_func_make_set::print(String *str, enum_query_type query_type)
{
  str->append(STRING_WITH_LEN("make_set("));
  item->print(str, query_type);
  if (arg_count)
  {
    str->append(',');
    print_args(str, 0, query_type);
  }
  str->append(')');
}

